import random
import string
import logging
import paramiko
import socket
from datetime import date, timedelta
from django.utils import timezone
from django.db.models import Count, Q
from celery import shared_task
from .models import Host, HostCredential, City, DataCenter, HostStatistics

logger = logging.getLogger(__name__)


def generate_random_password(length=16):
    """生成随机密码"""
    characters = string.ascii_letters + string.digits + "!@#$%^&*"
    return ''.join(random.choice(characters) for _ in range(length))


def change_host_password_via_ssh(host, old_password, new_password):
    """通过SSH连接修改主机密码"""
    ssh_client = None
    try:
        ssh_client = paramiko.SSHClient()
        ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        
        # 连接到主机
        ssh_client.connect(
            hostname=host.ip_address,
            port=host.ssh_port,
            username='root',
            password=old_password,
            timeout=30
        )
        
        # 执行密码修改命令
        # 注意：这里使用echo命令配合passwd，在生产环境中可能需要更安全的方式
        command = f"echo 'root:{new_password}' | chpasswd"
        stdin, stdout, stderr = ssh_client.exec_command(command)
        
        # 检查命令执行结果
        exit_status = stdout.channel.recv_exit_status()
        if exit_status == 0:
            logger.info(f"Password changed successfully for host {host.hostname}")
            return True, "Password changed successfully"
        else:
            error_msg = stderr.read().decode('utf-8')
            logger.error(f"Failed to change password for host {host.hostname}: {error_msg}")
            return False, error_msg
            
    except paramiko.AuthenticationException:
        error_msg = "Authentication failed - incorrect password"
        logger.error(f"Authentication failed for host {host.hostname}")
        return False, error_msg
    except paramiko.SSHException as e:
        error_msg = f"SSH connection error: {str(e)}"
        logger.error(f"SSH error for host {host.hostname}: {error_msg}")
        return False, error_msg
    except socket.timeout:
        error_msg = "Connection timeout"
        logger.error(f"Connection timeout for host {host.hostname}")
        return False, error_msg
    except Exception as e:
        error_msg = f"Unexpected error: {str(e)}"
        logger.error(f"Unexpected error for host {host.hostname}: {error_msg}")
        return False, error_msg
    finally:
        if ssh_client:
            ssh_client.close()


@shared_task
def change_host_passwords():
    """定期更新所有主机密码的任务（每8小时执行一次）"""
    logger.info("Starting password change task for all hosts")
    
    # 获取所有在线主机的凭证
    credentials = HostCredential.objects.filter(
        host__status='online'
    ).select_related('host')
    
    success_count = 0
    failure_count = 0
    
    for credential in credentials:
        try:
            host = credential.host
            old_password = credential.get_password()
            new_password = generate_random_password()
            
            logger.info(f"Attempting to change password for host {host.hostname}")
            
            # 尝试更改密码
            success, message = change_host_password_via_ssh(host, old_password, new_password)
            
            if success:
                # 密码更改成功，更新数据库
                credential.set_password(new_password)
                credential.save()
                success_count += 1
                logger.info(f"Password updated successfully for host {host.hostname}")
            else:
                failure_count += 1
                logger.error(f"Failed to update password for host {host.hostname}: {message}")
                
        except Exception as e:
            failure_count += 1
            logger.error(f"Error processing host {credential.host.hostname}: {str(e)}")
    
    logger.info(f"Password change task completed. Success: {success_count}, Failures: {failure_count}")
    return {
        'success_count': success_count,
        'failure_count': failure_count,
        'total_processed': success_count + failure_count
    }


@shared_task
def generate_daily_statistics():
    """生成每日主机统计数据的任务（每天00:00执行）"""
    today = date.today()
    logger.info(f"Generating daily statistics for {today}")
    
    # 获取所有机房
    datacenters = DataCenter.objects.select_related('city').all()
    
    statistics_created = 0
    
    for datacenter in datacenters:
        try:
            # 统计该机房的主机数量
            hosts_in_datacenter = Host.objects.filter(datacenter=datacenter)
            
            total_hosts = hosts_in_datacenter.count()
            online_hosts = hosts_in_datacenter.filter(status='online').count()
            offline_hosts = hosts_in_datacenter.filter(status='offline').count()
            maintenance_hosts = hosts_in_datacenter.filter(status='maintenance').count()
            
            # 创建或更新统计记录
            statistics, created = HostStatistics.objects.get_or_create(
                city=datacenter.city,
                datacenter=datacenter,
                statistics_date=today,
                defaults={
                    'total_hosts': total_hosts,
                    'online_hosts': online_hosts,
                    'offline_hosts': offline_hosts,
                    'maintenance_hosts': maintenance_hosts,
                }
            )
            
            if not created:
                # 如果记录已存在，更新数据
                statistics.total_hosts = total_hosts
                statistics.online_hosts = online_hosts
                statistics.offline_hosts = offline_hosts
                statistics.maintenance_hosts = maintenance_hosts
                statistics.save()
            
            statistics_created += 1
            logger.info(f"Statistics updated for {datacenter.city.name}-{datacenter.name}: "
                       f"Total: {total_hosts}, Online: {online_hosts}, "
                       f"Offline: {offline_hosts}, Maintenance: {maintenance_hosts}")
                       
        except Exception as e:
            logger.error(f"Error generating statistics for datacenter {datacenter.name}: {str(e)}")
    
    logger.info(f"Daily statistics generation completed. Records processed: {statistics_created}")
    return {
        'date': today.isoformat(),
        'statistics_created': statistics_created,
        'datacenters_processed': datacenters.count()
    }


@shared_task
def cleanup_old_statistics(days_to_keep=90):
    """清理超过指定天数的旧统计数据"""
    cutoff_date = date.today() - timedelta(days=days_to_keep)
    
    deleted_count, _ = HostStatistics.objects.filter(
        statistics_date__lt=cutoff_date
    ).delete()
    
    logger.info(f"Cleaned up {deleted_count} old statistics records older than {cutoff_date}")
    return {
        'deleted_count': deleted_count,
        'cutoff_date': cutoff_date.isoformat()
    }


@shared_task
def ping_all_hosts():
    """定期ping所有主机检查连通性"""
    from .views import HostViewSet
    
    logger.info("Starting ping check for all hosts")
    
    hosts = Host.objects.all()
    reachable_count = 0
    unreachable_count = 0
    
    # 创建视图集实例来使用ping方法
    host_viewset = HostViewSet()
    
    for host in hosts:
        try:
            result = host_viewset._ping_host(host)
            
            # 更新主机状态
            host.last_ping_time = result['ping_time']
            host.last_ping_result = result['is_reachable']
            
            if result['is_reachable']:
                reachable_count += 1
                # 如果主机从离线变为在线，更新状态
                if host.status == 'offline':
                    host.status = 'online'
            else:
                unreachable_count += 1
                # 如果主机连续无法ping通，标记为离线
                if host.status == 'online':
                    host.status = 'offline'
            
            host.save(update_fields=['last_ping_time', 'last_ping_result', 'status'])
            
        except Exception as e:
            logger.error(f"Error pinging host {host.hostname}: {str(e)}")
            unreachable_count += 1
    
    logger.info(f"Ping check completed. Reachable: {reachable_count}, Unreachable: {unreachable_count}")
    return {
        'reachable_count': reachable_count,
        'unreachable_count': unreachable_count,
        'total_checked': reachable_count + unreachable_count
    }
